package kcloud

import (
	"dustbe/util"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type saveFile struct {
	savePath string // 保存的路径
	fileName string // 文件真实名称（保存的文件名可能有重名，会用UUID + 文件名进行存储）
	leftTime int    // 剩余下载次数
	deadST   int64  // 销毁时间戳
}

var (
	saveFileManager map[string]*saveFile = make(map[string]*saveFile)
	scanCnt         int                  = 0   // 一个计数器，该文件中每个函数被调用计数+1。到 scanCycle 次会扫描一遍整个存储文件的文件夹来情况文件
	scanCycle       int                  = 20  // 每20次调用扫描一次
	deleteThred     int                  = 200 // 文件数量 超过这个数量就直接删除
)

func deleteByKey(key string) bool {
	saveFileObj, ok := saveFileManager[key]
	if ok {
		rm_err := os.Remove(saveFileObj.savePath)
		if rm_err != nil {
			return false
		}
		delete(saveFileManager, key)
		return true
	} else {
		return false
	}
}

func scanFileManager() {
	nowST := time.Now().Unix()
	for key, saveFileObj := range saveFileManager {
		// 超时了或者次数为0了 就直接删除
		if saveFileObj.deadST <= nowST || saveFileObj.leftTime <= 0 {
			deleteByKey(key)
		}
	}

	if len(saveFileManager) >= deleteThred {
		os.RemoveAll(storePath)
		os.Mkdir(storePath, 0777)
		for k := range saveFileManager {
			delete(saveFileManager, k)
		}
	}
}

func StoreUpload(c *gin.Context) {
	ok := util.LimitIpInterval(c, 10)
	if !ok {
		c.JSON(http.StatusOK, gin.H{"code": 4001, "data": "你刚刚传过文件，请不要短时间内多次传，我的服务器吃不消！"})
		return
	}
	key := c.PostForm("key")
	_, existKey := saveFileManager[key]
	if existKey { // 如果 缓存区存在关于key的记录，就去找一下这个文件是否真的存在
		targetFilePath := saveFileManager[key].savePath
		targetExist, _ := util.CheckFilePath(targetFilePath)
		if !targetExist { // 不存在就删除这个key
			deleteByKey(key)
		} else {
			c.JSON(http.StatusOK, gin.H{"code": 4005, "msg": "该密钥已经被注册过了，请换一个"})
			return
		}
	}

	durationHoursString := c.PostForm("duration")
	leftTimeString := c.PostForm("number")

	leftTime, _ := strconv.Atoi(leftTimeString)
	durationHours, _ := strconv.Atoi(durationHoursString)

	file, _ := c.FormFile("file")
	fileName := file.Filename

	nowST := time.Now().Unix()
	deadST := nowST + int64(durationHours)*3600
	savePath := storePath + "/" + uuid.New().String() + "-" + fileName

	// 存入文件
	if err := c.SaveUploadedFile(file, savePath); err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 4001, "msg": "文件写入服务器磁盘时出错"})
		return
	}

	// 记录一下，做个缓存
	fileObj := saveFile{
		savePath: savePath,
		fileName: fileName,
		leftTime: leftTime,
		deadST:   deadST,
	}

	saveFileManager[key] = &fileObj

	// 增加扫描计数
	scanCnt++
	if scanCnt == scanCycle { // 如果到了，就开启一个线程去扫描整个文件夹
		scanCnt = 0
		go scanFileManager()
	}

	// 反复确认信息
	c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{
		"key":      key,
		"duration": durationHours,
		"number":   leftTime,
	}})
}

func StoreDownload(c *gin.Context) {
	ok := util.LimitIpInterval(c, 10)
	if !ok {
		c.JSON(http.StatusOK, gin.H{"code": 4001, "data": "你刚刚下载过文件，请不要短时间内多次下载，我的服务器吃不消！"})
		return
	}

	key := c.Query("key")
	_, keyExist := saveFileManager[key]
	if !keyExist {
		c.JSON(http.StatusOK, gin.H{"code": 4002, "msg": "没有找到密钥为" + key + "的文件"})
		return
	}

	// 先看是不是过时了或者次数超了
	leftTime := saveFileManager[key].leftTime - 1
	saveFileManager[key].leftTime--

	if saveFileManager[key].leftTime <= 0 {
		defer deleteByKey(key)
		if saveFileManager[key].leftTime < 0 {
			c.JSON(http.StatusOK, gin.H{"code": 4002, "msg": "下载次数超过了，已经删除！"})
			return
		}
	}

	if time.Now().Unix() >= saveFileManager[key].deadST {
		defer deleteByKey(key)
		c.JSON(http.StatusOK, gin.H{"code": 4002, "msg": "下载超时了，已经删除！"})
		return
	}

	// 增加扫描计数
	scanCnt++
	if scanCnt == scanCycle { // 如果到了，就开启一个线程去扫描整个文件夹
		scanCnt = 0
		go scanFileManager()
	}

	savePath := saveFileManager[key].savePath
	fileName := saveFileManager[key].fileName
	fileByte, err := ioutil.ReadFile(savePath)
	fileLength := len(fileByte)
	fileLengthString := strconv.Itoa(fileLength)

	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 4002, "msg": "服务器文件打开错误！可能是服务器的自我防护进程被触发导致所有存储文件被删除！"})
		return
	}

	c.Writer.Header().Add("length", fileLengthString)

	c.JSON(http.StatusOK, gin.H{"code": 200, "data": gin.H{
		"leftTime": leftTime,
		"fileName": fileName,
		"base64":   fileByte,
	}})
}
